/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2018年11月22日
 * V4.0
 */
package com.jphenix.share.tools;

import com.jphenix.share.lang.SString;
import com.jphenix.share.lang.StringSorter;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.BytesArrayUtil;
import com.jphenix.standard.docs.ClassInfo;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

/**
 * RSA非对称加密处理类
 * 
 * com.jphenix.share.tools.Rsa
 * 
 * 2018-11-28 修改了发现的错误。增加了PFX格式，以及CER格式的证书文件支持 2018-11-29 修改了获取公钥对象时的错误
 * 增加了输出RF2405标准的Base64编码结果 增加了sha1编码后转16进制 2018-12-20
 * 修改了用私钥解密方法，增加了是否在解密后做Base64解码
 * 
 * 2019-07-27 增加了一种用公钥证书加密的方法 getStrByPublic
 * 2020-01-16 整理了注释
 * 2020-01-17 增加了生成HMACSHA256签名
 * 
 * @author MBG 2018年11月22日
 */
@ClassInfo({ "2020-01-17 10:22", "RSA非对称加密处理类" })
public class Rsa {

	private boolean    outRf2405     = false;                  // 是否按照RF2405标准输出Base64编码的结果（带回车符）
	private String     pubKeyStr     = null;                   // 公钥字符串
	private String     privateKeyStr = null;                   // 私钥字符串
	private PrivateKey priKey        = null;                   // 私钥对象
	private PublicKey  pubKey        = null;                   // 公钥对象
	private String     encoding      = "UTF-8";                // 字符串编码
	private String     encryptType   = "RSA/ECB/PKCS1Padding"; // 加密方式
	private int        outType       = 2;                      // 输出类型 0直接转换成字符串（不要用0，容易出错）1转换成16进制字符串 2转换成base64

	/**
	 * 构造函数
	 * @author MBG
	 */
	public Rsa() {
		super();
	}

	/**
	 * 构造函数
	 * @param puKey 公钥字符串
	 * @param prKey 私钥字符串
	 * @param enc   加解密内容编码格式
	 * @author MBG
	 */
	public Rsa(String puKey, String prKey, String enc) {
		super();
		pubKeyStr = puKey;
		privateKeyStr = prKey;
		if (enc != null && enc.length() > 0) {
			encoding = enc;
		}
	}

	/**
	 * 执行公钥加密
	 * @param content    需要加密的内容字符串
	 * @param pubKey     公钥字符串（Base64后的）
	 * @return           加密后的字符串
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public static String enc(String content, String pubKey) throws Exception {
		return (new Rsa(pubKey, null, null)).enc(content);
	}

	/**
	 * 执行公钥加密 （RF2405标准）
	 * @param content    需要加密的内容字符串
	 * @param pubKey     公钥字符串（Base64后的）
	 * @return           加密后的字符串
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public static String rf2405Enc(String content, String pubKey) throws Exception {
		return (new Rsa(pubKey, null, null)).setOutRf2405(true).enc(content);
	}

	/**
	 * 执行公钥加密
	 * @param content     加密内容
	 * @return            加密后的字符串
	 * @throws Exception  异常 2018年11月22日
	 * @author MBG
	 */
	public String enc(String content) throws Exception {
		if (outType == 1) {
			return BytesArrayUtil.byte2Hex(encrypt(content));
		} else if (outType == 2) {
			if (outRf2405) {
				return Base64.rfc2405Encode(encrypt(content));
			}
			return Base64.base64Encode(encrypt(content));
		}
		return new String(encrypt(content));
	}

	/**
	 * 执行公钥加密
	 * @param content     加密内容
	 * @return            加密后的字节数组
	 * @throws Exception  异常 2018年11月22日
	 * @author MBG
	 */
	public byte[] encrypt(String content) throws Exception {
		if (content == null) {
			// 这叫以牙还牙
			return null;
		}
		return encrypt(content.getBytes());
	}

	/**
	 * 执行公钥加密
	 * @param content    需要加密的内容字节数组
	 * @return           加密后的字节数组
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public byte[] encrypt(byte[] content) throws Exception {
		return encrypt(content, getPublicKey(pubKeyStr));
	}

	/**
	 * 执行公钥加密
	 * @param content    需要加密的字节数组
	 * @param publicKey  公钥对象
	 * @return           加密后的字节数组
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	private byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception {
		// 构建加密处理类
		Cipher cipher = Cipher.getInstance(encryptType);
		// 执行初始化
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		// 分段加密
		int blockSize = cipher.getOutputSize(content.length) - 11;
		byte[] encryptedData = null; // 构建返回值
		for (int i = 0; i < content.length; i += blockSize) {
			// 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
			byte[] doFinal = cipher.doFinal(subArray(content, i, i + blockSize));
			encryptedData = addAll(encryptedData, doFinal);
		}
		return encryptedData;
	}

	/**
	 * 构建公钥对象 X509 格式
	 * @param pubStr     公钥字符串（Base64编码后的）
	 * @return           公钥对象
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	private PublicKey getPublicKey(String pubStr) throws Exception {
		if (pubKey == null) {
			CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
			Certificate certificate = certificateFactory
					.generateCertificate(new ByteArrayInputStream(Base64.decode64(pubStr)));
			return certificate.getPublicKey();
		}
		return pubKey;
	}

	/**
	 * 根据公钥证书文件全路径设置公钥证书对象
	 * @param pubCerPath 公钥证书全路径
	 * @return           当前类实例
	 * @throws Exception 异常 2018年11月28日
	 * @author MBG
	 */
	public Rsa setPublicKeyFromFile(String pubCerPath) throws Exception {
		FileInputStream pubKeyStream = null; // 公钥文件读入流
		try {
			pubKeyStream = new FileInputStream(pubCerPath);
			byte[] reads = new byte[pubKeyStream.available()];
			pubKeyStream.read(reads);
			setPublicKeyByText(new String(reads));
		} catch (Exception e) {
			throw e;
		} finally {
			if (pubKeyStream != null) {
				try {
					pubKeyStream.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return this;
	}

	/**
	 * 通过公钥证书中的内容设置公钥对象
	 * @param pubKeyText 公钥证书中的内容
	 * @return           当前类实例
	 * @throws Exception 异常 2018年11月28日
	 * @author MBG
	 */
	public Rsa setPublicKeyByText(String pubKeyText) throws Exception {
		CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
		BufferedReader br = new BufferedReader(new StringReader(pubKeyText));
		String line = null;
		StringBuilder keyBuffer = new StringBuilder();
		while ((line = br.readLine()) != null) {
			if (!line.startsWith("-")) {
				keyBuffer.append(line);
			}
		}
		Certificate certificate = 
				certificateFactory.generateCertificate(
						new ByteArrayInputStream(Base64.decode64(keyBuffer.toString())));
		pubKey = certificate.getPublicKey();
		return this;
	}

	/**
	 * 执行私钥解密
	 * @param content       需要解密的字符串
	 * @param privateKeyStr 私钥字符串
	 * @param b64Dec        解密后是否需要做Base64解码
	 * @return              解密后的字符串
	 * @throws Exception    异常 2018年11月22日
	 * @author MBG
	 */
	public static String dec(String content, String privateKeyStr, boolean b64Dec) throws Exception {
		return (new Rsa(null, privateKeyStr, null)).dec(content, b64Dec);
	}

	/**
	 * 执行私钥解密
	 * @param content    需要解密的字符串
	 * @param b64Dec     解密后是否需要做Base64解码
	 * @return           解密后的字符串
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public String dec(String content, boolean b64Dec) throws Exception {
		if (b64Dec) {
			return new String(Base64.decode(decrypt(content)), encoding);
		}
		return new String(decrypt(content), encoding);
	}

	/**
	 * 执行私钥解密
	 * @param content    需要解密的内容字符串
	 * @return           解密后的字节数组
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public byte[] decrypt(String content) throws Exception {
		if (content == null) {
			return null; // 以牙还牙
		}
		if (outType == 1) {
			return decrypt(BytesArrayUtil.hex2Bytes(content));
		} else if (outType == 2) {
			return decrypt(Base64.decode64(content));
		}
		if (encoding != null && encoding.length() > 0) {
			return decrypt(content.getBytes(encoding));
		}
		return decrypt(content.getBytes());
	}

	/**
	 * 执行私钥解密
	 * @param content    需要解密的内容字节数组
	 * @return           解密后的内容数组
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public byte[] decrypt(byte[] content) throws Exception {
		return decrypt(content, getPrivateKey(privateKeyStr));
	}

	/**
	 * 执行私钥解密
	 * @param content    需要解密的内容字节数组
	 * @param privateKey 私钥对象
	 * @return           解密后的内容数组
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	private byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception {
		// 构建解密处理类
		Cipher cipher = Cipher.getInstance(encryptType);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		// 分段加密
		int blockSize = cipher.getOutputSize(content.length);
		byte[] decryptData = null;
		for (int i = 0; i < content.length; i += blockSize) {
			byte[] doFinal = cipher.doFinal(subArray(content, i, i + blockSize));

			decryptData = addAll(decryptData, doFinal);
		}
		return decryptData;
	}

	/**
	 * 获取私钥对象 PKCS#8 格式
	 * @param privateKeyStr 私钥字符串
	 * @return              私钥对象
	 * @throws Exception    异常 2018年11月22日
	 * @author MBG
	 */
	private PrivateKey getPrivateKey(String privateKeyStr) throws Exception {
		if (priKey == null) {
			// 母鸡
			PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode64(privateKeyStr));
			// 获取密钥工厂类
			KeyFactory kf = KeyFactory.getInstance("RSA");
			priKey = kf.generatePrivate(spec);
		}
		return priKey;
	}

	/**
	 * 设置私钥字符串
	 * @param keyContent 私钥内容（不带换行符，标识行）
	 * @return           当前类实例
	 * @throws Exception 异常 2018年11月28日
	 * @author MBG
	 */
	public Rsa setPrivateKey(String keyContent) throws Exception {
		priKey = getPrivateKey(keyContent);
		return this;
	}

	/**
	 * 执行签名
	 * @param content       待签名内容
	 * @param privateKeyStr 私钥字符串
	 * @return              签名串
	 * @throws Exception    异常 2018年11月22日
	 * @author MBG
	 */
	public static String sign(String content, String privateKeyStr) throws Exception {
		return (new Rsa(null, privateKeyStr, null)).sign(content);
	}

	/**
	 * 执行签名 （RF2405标准）
	 * @param content       待签名内容
	 * @param privateKeyStr 私钥字符串
	 * @return              签名串
	 * @throws Exception    异常 2018年11月22日
	 * @author MBG
	 */
	public static String rf2405Sign(String content, String privateKeyStr) throws Exception {
		return (new Rsa(null, privateKeyStr, null)).setOutRf2405(true).sign(content);
	}

	/**
	 * 获取准备签名的字符串
	 * @param paraMap    请求参数容器
	 * @param verfySbf   带签名的字符串
	 * @param excludeKey 需要排除的参数主键序列（签名串主键）
	 * @return           准备签名的字符串 2017年7月2日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	public static String getSignData(Map paraMap, List<String> excludeKey) {
		if (paraMap == null) {
			return "";
		}
		// 构建返回值
		StringBuffer verfySbf = new StringBuffer();
		// 获取请求参数（有小到大排序）
		List<String> keyList = StringSorter.asc(BaseUtil.getMapKeyList(paraMap));
		boolean noFirst = false; // 是否不是首次循环
		for (String keyEle : keyList) {
			if (excludeKey != null && excludeKey.contains(keyEle)) {
				continue;
			}
			if (noFirst) {
				verfySbf.append("&");
			} else {
				noFirst = true;
			}
			verfySbf.append(keyEle).append("=").append(SString.valueOf(paraMap.get(keyEle)));
		}
		return verfySbf.toString();
	}

	/**
	 * 执行签名
	 * @param data       需要签名的数据对照容器，按照key字符串ascii码由小到大排序
	 * @param excludeKey 需要排除的key序列
	 * @return           签名串
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	public String sign(Map data, List<String> excludeKey) throws Exception {
		return sign(getSignData(data, excludeKey));
	}

	/**
	 * 执行签名
	 * @param data          需要签名的数据对照容器，按照key字符串ascii码由小到大排序
	 * @param excludeKey    需要排除的key序列
	 * @param privateKeyStr 私钥字符串
	 * @return              签名串
	 * @throws Exception    异常 2018年11月22日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	public static String sign(Map data, List<String> excludeKey, String privateKeyStr) throws Exception {
		return (new Rsa(null, privateKeyStr, null)).sign(data, excludeKey);
	}

	/**
	 * 执行签名 (RF2405标准）
	 * @param data          需要签名的数据对照容器，按照key字符串ascii码由小到大排序
	 * @param excludeKey    需要排除的key序列
	 * @param privateKeyStr 私钥字符串
	 * @return              签名串
	 * @throws Exception    异常 2018年11月22日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	public static String rf2405Sign(Map data, List<String> excludeKey, String privateKeyStr) throws Exception {
		return (new Rsa(null, privateKeyStr, null)).setOutRf2405(true).sign(data, excludeKey);
	}

	/**
	 * 执行签名
	 * @param content    待签名字符串
	 * @return           签名串
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public String sign(String content) throws Exception {
		if (content == null) {
			return null;
		}
		if (outType == 1) {
			return BytesArrayUtil.byte2Hex(doSign(content));
		} else if (outType == 2) {
			if (outRf2405) {
				return Base64.rfc2405Encode(doSign(content));
			}
			return Base64.base64Encode(doSign(content));
		}
		return new String(doSign(content));
	}

	/**
	 * 执行签名
	 * @param content    待签名字符串
	 * @return           签名串字节数组
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public byte[] doSign(String content) throws Exception {
		if (content == null) {
			return null;
		}
		return doSign(content.getBytes(encoding));
	}

	/**
	 * 执行签名
	 * @param content    签名内容字节数组
	 * @return           签名串
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public byte[] doSign(byte[] content) throws Exception {
		return doSign(content, getPrivateKey(privateKeyStr));
	}

	/**
	 * 执行签名
	 * @param content     需要签名的内容
	 * @param privateKey  私钥对象
	 * @return            签名串字节数组
	 * @throws Exception  异常 2018年11月22日
	 * @author MBG
	 */
	private byte[] doSign(byte[] content, PrivateKey privateKey) throws Exception {
		// 构建签名处理类
		Signature signature = Signature.getInstance("SHA1WithRSA");
		signature.initSign(privateKey);
		signature.update(content);
		return signature.sign();
	}

	/**
	 * 执行公钥验签
	 * @param data         需要验签的数据容器
	 * @param excludeKey   需要排除的key序列
	 * @param sign         签名串
	 * @param publicKeyStr 公钥字符串
	 * @return             签名结果
	 * @throws Exception   异常 2018年11月22日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	public static boolean verfySign(Map data, List<String> excludeKey, String sign, String publicKeyStr)
			throws Exception {
		return (new Rsa(publicKeyStr, null, null)).verfySign(data, excludeKey, sign);
	}

	/**
	 * 执行公钥验签
	 * @param verfyData    需要验签的数据字符串
	 * @param sign         签名串
	 * @param publicKeyStr 公钥字符串
	 * @return             验签结果
	 * @throws Exception   异常 2018年11月22日
	 * @author MBG
	 */
	public static boolean verfySign(String verfyData, String sign, String publicKeyStr) throws Exception {
		return (new Rsa(publicKeyStr, null, null)).verfySign(verfyData, sign);
	}

	/**
	 * 执行公钥验签
	 * @param data        需要验签的数据容器
	 * @param excludeKey  需要排除的key序列
	 * @param sign        签名串
	 * @return            签名结果
	 * @throws Exception  异常 2018年11月22日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	public boolean verfySign(Map data, List<String> excludeKey, String sign) throws Exception {
		return verfySign(getSignData(data, excludeKey), sign);
	}

	/**
	 * 执行公钥验签
	 * @param verfyData  需要验签的数据字符串
	 * @param sign       签名串
	 * @return           验签结果
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	public boolean verfySign(String verfyData, String sign) throws Exception {
		return verfySign(verfyData, sign, getPublicKey(pubKeyStr));
	}

	/**
	 * 执行公钥验签
	 * @param verfyData  需要验签的数据字符串
	 * @param sign       签名串
	 * @param publicKey  公钥对象
	 * @return           验签结果
	 * @throws Exception 异常 2018年11月22日
	 * @author MBG
	 */
	private boolean verfySign(String verfyData, String sign, PublicKey publicKey) throws Exception {
		if (verfyData == null || sign == null) {
			return false;
		}
		// 有时候客户端服务器提交过来的验签值，类似证书的方式，其中带换行符
		// 在这里将换行符去掉
		sign = BaseUtil.swapString(sign, "\r", "", "\n", "");

		byte[] signBytes; // 签名信息
		if (outType == 1) {
			signBytes = BytesArrayUtil.hex2Bytes(sign);
		} else if (outType == 2) {
			signBytes = Base64.decode64(sign);
		} else if (encoding != null && encoding.length() > 0) {
			signBytes = sign.getBytes(encoding);
		} else {
			signBytes = sign.getBytes();
		}
		// 获取指定客户机公钥
		Signature signature = Signature.getInstance("SHA1WithRSA");
		signature.initVerify(publicKey);
		signature.update(verfyData.getBytes(encoding));
		return signature.verify(signBytes);
	}

	/**
	 * 设置PFX私钥证书文件
	 * @param path       文件全路径
	 * @param password   证书密码
	 * @return           当前类实例
	 * @throws Exception 异常 2018年11月28日
	 * @author MBG
	 */
	public Rsa setPfxFile(String path, String password) throws Exception {
		priKey = getPrivateKeyByPfx(FileCopyTools.copyToByteArray(new File(path)), password);
		return this;
	}

	/**
	 * 设置公钥信息
	 * @param keyContent 公钥内容（去掉换行符）
	 * @return           当前类实例
	 * @throws Exception 异常 2018年11月28日
	 * @author MBG
	 */
	public Rsa setPublicKey(String keyContent) throws Exception {
		pubKey = getPublicKey(keyContent);
		return this;
	}

	/**
	 * 根据pfx证书获取keyStore
	 * @param pfxData    私钥证书文件数据
	 * @param password   私钥密码
	 * @return           密钥存储类
	 * @throws Exception 异常
	 */
	private KeyStore getKeyStore(byte[] pfxData, String password) throws Exception {
		KeyStore keystore = KeyStore.getInstance("PKCS12");
		keystore.load(new ByteArrayInputStream(pfxData), password.toCharArray());
		return keystore;
	}

	/**
	 * 根据pfx证书得到私钥
	 * @param pfxData    私钥证书文件数据
	 * @param password   私钥密码
	 * @throws Exception 异常
	 */
	private PrivateKey getPrivateKeyByPfx(byte[] pfxData, String password) throws Exception {
		PrivateKey privateKey = null;
		KeyStore keystore = getKeyStore(pfxData, password);
		Enumeration<String> enums = keystore.aliases();
		String keyAlias = "";
		while (enums.hasMoreElements()) {
			keyAlias = enums.nextElement();
			if (keystore.isKeyEntry(keyAlias)) {
				privateKey = (PrivateKey) keystore.getKey(keyAlias, password.toCharArray());
			}
		}
		return privateKey;
	}

	/**
	 * 设置加密方式
	 * @param type 加密方式 2018年11月28日
	 * @author MBG
	 */
	public Rsa setEncryptType(String type) {
		if (type == null || type.length() < 1) {
			return this;
		}
		encryptType = type;
		return this;
	}

	/**
	 * 截取数组
	 * @param array 待截取的数组
	 * @param start 起始位置
	 * @param end   结束位置
	 * @return      截取到的数组 2018年11月28日
	 * @author MBG
	 */
	private byte[] subArray(byte[] array, int start, int end) {
		if (array == null) {
			return null;
		}
		if (start < 0) {
			start = 0;
		}
		if (end > array.length) {
			end = array.length;
		}
		int newSize = end - start;

		if (newSize <= 0) {
			return new byte[0];
		}
		byte[] subarray = new byte[newSize];
		System.arraycopy(array, start, subarray, 0, newSize);
		return subarray;
	}

	/**
	 * 合并两个数组
	 * @param array1 数组1
	 * @param array2 数组2
	 * @return       合并后的数组 2018年11月28日
	 * @author MBG
	 */
	private byte[] addAll(byte[] array1, byte[] array2) {
		if (array1 == null && array2 == null) {
			return new byte[0];
		}
		if (array1 == null) {
			return array2.clone();
		} else if (array2 == null) {
			return array1.clone();
		}
		byte[] joinedArray = new byte[array1.length + array2.length];
		System.arraycopy(array1, 0, joinedArray, 0, array1.length);
		System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
		return joinedArray;
	}

	/**
	 * 设置输出类型
	 * @param type 输出类型 0直接转换成字符串（不要用0，容易出错） 1转换成16进制字符串 2转换成base64
	 * @return     当前类实例 2018年11月28日
	 * @author MBG
	 */
	public Rsa setOutType(int type) {
		this.outType = type;
		return this;
	}

	/**
	 * 设置是否按照RF2405标准输出Base64编码的结果（带回车符）
	 * @param enabled 是否按照RF2405标准输出Base64编码的结果（带回车符）
	 * @return        当前类实例 2018年11月29日
	 * @author MBG
	 */
	public Rsa setOutRf2405(boolean enabled) {
		outRf2405 = enabled;
		return this;
	}

	/**
	 * sha1计算后进行16进制转换
	 * @param data 待计算的数据
	 * @return     计算结果
	 * @throws     异常
	 */
	public static String sha1Hex(String data) throws Exception {
		return sha1Hex(data, null);
	}

	/**
	 * sha1计算后进行16进制转换
	 * @param data     待计算的数据
	 * @param encoding 编码
	 * @return         计算结果
	 * @throws         异常
	 */
	public static String sha1Hex(String data, String enc) throws Exception {
		if (enc == null || enc.length() < 1) {
			enc = "UTF-8";
		}
		byte[] bytes = sha1(data.getBytes(enc));
		return BytesArrayUtil.byte2Hex(bytes);
	}

	/**
	 * sha1计算.
	 * @param data 待计算的数据
	 * @return     计算结果
	 */
	public static byte[] sha1(byte[] data) {
		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("SHA-1");
			md.reset();
			md.update(data);
			return md.digest();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 使用公钥证书加密
	 * @param publickey  公钥证书内容
	 * @param data       需要加密的数据
	 * @return           加密后的数据
	 * @throws Exception 异常
	 * 2019年7月27日
	 * @author MBG
	 */
	public static String getStrByPublic(String publickey, String data) throws Exception {
	  byte[] encryData = encryptByPublicKey(data.getBytes(StandardCharsets.UTF_8), publickey);
	  String reData = URLEncoder.encode(Base64.base64Encode(encryData),"UTF-8");
	  return Base64.base64Encode(reData.getBytes(StandardCharsets.UTF_8));
    }

	/**
	 * 用公钥加密
	 * @param data       加密数据
	 * @param key        密钥
	 * @return           加密后的内容
	 * @throws Exception 异常
	 */
	public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
		// 对公钥解密
		byte[] keyBytes = Base64.decode64(key);
		// 取公钥
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		// 对数据解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		return cipher.doFinal(data);
	}
	
	
    /**
     * 生成HMACSHA256签名
     * @param data        待处理数据
     * @param key         密钥
     * @return            加密结果
     * @throws Exception  异常
     */
    public static String signHMACSHA256(String data, String key) throws Exception {
        Mac sha256HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256HMAC.init(secretKey);
        byte[] array = sha256HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sbf = new StringBuilder();
        for (byte item : array) {
        	sbf.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sbf.toString().toUpperCase();
    }
}
